Fork me on GitHub

FlashMapManager

注意:所有文章除特别说明外,转载请注明出处.

第20章 FlashMapManager

FlashMapManager用来管理FlashMap,FlashMap用于在redirect时传递参数。FlashMapManager实现结构简单,只有一个抽象类和一个实现类。抽象类采用模板模式定义整体流程。具体实现类SessionFlashManager通过模板方法提供具体操作FlashMap的功能。

说明:1.实际的Session中保存的FlashMap类型是List类型,也就是说一个Session可以保存多个FlashMap,一个FlashMap保存着一套Redirect转发所传递的参数。2. FlashMap继承自HashMap,除了用于HashMap的功能和设置有效期,还可以保存Redirect后的目标路径和通过url传递的参数,这两项内容主要用来从Session保存的多个FlashMap中查找当前的FalshMap。

1.AbstractFlashMapManager

1.1 saveOutputFlashMap

public final void saveOutputFlashMap(FlashMap flashMap, HttpServletRequest request, HttpServletResponse response) {
    if (!CollectionUtils.isEmpty(flashMap)) {
        //首先对flashMap中转发的地址和参数进行编码,这里的request主要是用来获取当前的编码
        String path = this.decodeAndNormalizePath(flashMap.getTargetRequestPath(), request);            
        flashMap.setTargetRequestPath(path);
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Saving FlashMap=" + flashMap);
        }
        //设置过期时间,默认值是180秒
        flashMap.startExpirationPeriod(this.getFlashMapTimeout());
        用于获取互斥变量,是模板方法,如果子类返回不为null则同步执行,否则不需要同步
        Object mutex = this.getFlashMapsMutex(request);
        if (mutex != null) {
            synchronized(mutex) {
                //取回保存的flashMap,如果没有则新建一个
                List<FlashMap> allFlashMaps = this.retrieveFlashMaps(request);
                List<FlashMap> allFlashMaps = allFlashMaps != null ? allFlashMaps : new CopyOnWriteArrayList();
                ((List)allFlashMaps).add(flashMap);
                //将添加完的list<FalshMap>更新到存储介质中去,是模板方法,由子类实现
                this.updateFlashMaps((List)allFlashMaps, request, response);
            }
        } else {
            List<FlashMap> allFlashMaps = this.retrieveFlashMaps(request);
            List<FlashMap> allFlashMaps = allFlashMaps != null ? allFlashMaps : new LinkedList();
            ((List)allFlashMaps).add(flashMap);
            this.updateFlashMaps((List)allFlashMaps, request, response);
        }

    }
}

总结:1.首先对flashMap中的目标地址和url参数进行编码,编码格式使用当前request获取。2.然后设置有效期,通过flashMapTimeout参数配置。3.最后将flashMap添加到整体的List中并更新。

1.2 SessionFlashMapManager

public class SessionFlashMapManager extends AbstractFlashMapManager {
    private static final String FLASH_MAPS_SESSION_ATTRIBUTE = SessionFlashMapManager.class.getName() + ".FLASH_MAPS";

    public SessionFlashMapManager() {
    }

    @Nullable
    protected List<FlashMap> retrieveFlashMaps(HttpServletRequest request) {
        HttpSession session = request.getSession(false);
        return session != null ? (List)session.getAttribute(FLASH_MAPS_SESSION_ATTRIBUTE) : null;
    }

    protected void updateFlashMaps(List<FlashMap> flashMaps, HttpServletRequest request, HttpServletResponse response) {
        WebUtils.setSessionAttribute(request, FLASH_MAPS_SESSION_ATTRIBUTE, !flashMaps.isEmpty() ? flashMaps : null);
    }

    protected Object getFlashMapsMutex(HttpServletRequest request) {
        return WebUtils.getSessionMutex(request.getSession());
    }
}

1.3 retrieveAndUpdate

public final FlashMap retrieveAndUpdate(HttpServletRequest request, HttpServletResponse response) {
     //从存储介质session中获取list<FlashMap>,模板方法,子类实现
    List<FlashMap> allFlashMaps = this.retrieveFlashMaps(request);
    if (CollectionUtils.isEmpty(allFlashMaps)) {
        return null;
    } else {
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Retrieved FlashMap(s): " + allFlashMaps);
        }
        //检查过期的flashMap,并将那他们设置到mapsToRemove
        List<FlashMap> mapsToRemove = this.getExpiredFlashMaps(allFlashMaps);
        //获取与当前request匹配的FlashMap,并设置到match中
        FlashMap match = this.getMatchingFlashMap(allFlashMaps, request);
        //如果有匹配的则将其添加到mapsToRemove,待下面删除
        if (match != null) {
            mapsToRemove.add(match);
        }
        //删除mapsToRemove中保存的变量
        if (!mapsToRemove.isEmpty()) {
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Removing FlashMap(s): " + mapsToRemove);
            }

            Object mutex = this.getFlashMapsMutex(request);
            if (mutex != null) {
                synchronized(mutex) {
                    allFlashMaps = this.retrieveFlashMaps(request);
                    if (allFlashMaps != null) {
                        allFlashMaps.removeAll(mapsToRemove);
                        this.updateFlashMaps(allFlashMaps, request, response);
                    }
                }
            } else {
                allFlashMaps.removeAll(mapsToRemove);
                this.updateFlashMaps(allFlashMaps, request, response);
            }
        }
        return match;
    }
}

总结:上面的过程是:1.首先使用retrieveFlashMaps模板方法获取List。2.然后检查其中已经过期的FlashMap并保存,检查方法通过保存时设置的过期时间进行判断。3.然后调用getMatchingFlashMap()方法从获取的List中找出和当前request相匹配的FlashMap。4.最后将过期的和当前请求匹配的FlashMap从List中删除并更新到Session中,将与当前request匹配的返回。

1.4 getMatchingFlashMap()

该方法是查找与当前request匹配的FlashMap。

private FlashMap getMatchingFlashMap (List<FlashMap> allMaps, HttpServletRequest request) {
    List<FlashMap> result = new LinkedList<FlashMap>();
    for(FlashMap flashMap : allMaps){
        //1.调用isFlashMapForRequest()方法实际检查是否匹配,如果匹配则保存到临时变量result
        if(isFlashMapForRequest(flashMap, request)){
            result.add(flashMap);
        }
    }

    if(!result.isEmpty()){
        Collections.sort(result);
        if(logger.isDebugEnable){
            logger.debug("");
        }
        return result.get(0);
    }
    return null;
}

1.5 isFlashMapForRequest()

protected boolean isFlashMapForRequest(FlashMap flashMap, HttpServletRequest request) {
    //检查目标路径,如果flashMap中保存的和request的不匹配,返回false
    String expectedPath = flash.getTargetRequestpath();
    if(expectedPath != null){
        String requestUri = getUrlPathHelper().getOriginatingRequestUri(request);
        if(!requestUri.equals(expectedPath) && !requestUri.equals(expectedPath + "/")){
            return false;
        }
    }

    //检查参数,如果在FlashMap中保存的参数在request中没有则返回false
    MultiValueMap<String, String> targetParams = flashMap.getTargetRequestParams();
    for(String expectedName : targetParams.keySet()){
        if(!ObjectUtils.containsElement(request.getParameterValues(expectedName), expectedValue)){
            return false;
        }
    }

    return true;
}

总结:这里的检查方法是通过FlashMap中保存的目标地址和url参数与request进行比较的。

本文标题:FlashMapManager

文章作者:Bangjin-Hu

发布时间:2019年10月15日 - 09:22:26

最后更新:2020年03月30日 - 08:17:51

原始链接:http://bangjinhu.github.io/undefined/第20章 FlashMapManager/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

Bangjin-Hu wechat
欢迎扫码关注微信公众号,订阅我的微信公众号.
坚持原创技术分享,您的支持是我创作的动力.